package junior.db.service;

import junior.db.dao.base.IBaseId;
import junior.db.dao.jdbc.JdbcDao;
import junior.db.dao.mybatis.base.BaseMapper;
import junior.db.dao.update.BatchInsertOption;
import junior.db.dao.update.BatchUpdateOption;
import junior.db.dao.update.BatchUpsertOption;
import junior.db.dao.update.DeleteOption;
import junior.db.dao.update.InsertOption;
import junior.db.dao.update.UpdateOption;
import junior.db.dao.update.UpdateOptionHelper;
import junior.db.dao.update.UpsertOption;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author wubo
 * @date 2024/9/6 16:33
 */
public abstract class EntityService<ID, PO extends IBaseId, VO, DTO, Mapper extends BaseMapper<PO, ID>> extends AbstractService<ID, PO, VO, Mapper> implements IEntityService<ID, PO, VO, DTO> {
    private JdbcDao jdbcDao;
    
    public void setJdbcDao(JdbcDao jdbcDao) {
        this.jdbcDao = jdbcDao;
    }
    
    @Override
    public int insert(DTO dto) {
        return insert(dto, getInsertOption(dto));
    }
    
    @Override
    public int insert(DTO dto, InsertOption option) {
        PO po = dtoToPO(dto);
        int affectedRows = getMapper().insert(po, option);
        afterInsert(dto, po, affectedRows);
        return affectedRows;
    }
    
    @Override
    public int batchInsert(List<DTO> dtoList) {
        return batchInsert(dtoList, getBatchInsertOption(dtoList));
    }
    
    @Override
    public int batchInsert(List<DTO> dtoList, BatchInsertOption option) {
        List<PO> poList = dtoList.stream().map(this::dtoToPO).collect(Collectors.toList());
        int affectedRows;
        if (jdbcDao != null) {
            int[] affectedRowArray = jdbcDao.batchInsert(poList, option);
            affectedRows = Arrays.stream(affectedRowArray).sum();
        } else {
            affectedRows = getMapper().batchInsert(poList, option);
        }
        afterBatchInsert(dtoList, poList, affectedRows);
        return affectedRows;
    }
    
    @Override
    public int upsert(DTO dto, String... updateFields) {
        return upsert(dto, getUpsertOption(dto, updateFields));
    }
    
    @Override
    public int upsert(DTO dto, UpsertOption option) {
        PO po = dtoToPO(dto);
        int affectedRows = getMapper().upsert(po, option);
        afterUpsert(dto, po, affectedRows);
        return affectedRows;
    }
    
    @Override
    public int batchUpsert(List<DTO> dtoList, String... updateFields) {
        return batchUpsert(dtoList, getBatchUpsertOption(dtoList, updateFields));
    }
    
    @Override
    public int batchUpsert(List<DTO> dtoList, BatchUpsertOption option) {
        List<PO> poList = dtoList.stream().map(this::dtoToPO).collect(Collectors.toList());
        int affectedRows;
        if (jdbcDao != null) {
            int[] affectedRowArray = jdbcDao.batchUpsert(poList, option);
            affectedRows = Arrays.stream(affectedRowArray).sum();
        } else {
            affectedRows = getMapper().batchUpsert(poList, option);
        }
        afterBatchInsert(dtoList, poList, affectedRows);
        return affectedRows;
    }
    
    @Override
    public int update(DTO dto, String... updateFields) {
        return update(dto, getUpdateOption(dto, updateFields));
    }
    
    @Override
    public int update(DTO dto, UpdateOption option) {
        PO po = dtoToPO(dto);
        int affectedRows = getMapper().update(po, option);
        afterUpdate(dto, po, affectedRows);
        return affectedRows;
    }
    
    @Override
    public int batchUpdate(List<DTO> dtoList, String... updateFields) {
        return batchUpdate(dtoList, getBatchUpdateOption(dtoList, updateFields));
    }
    
    @Override
    public int batchUpdate(List<DTO> dtoList, BatchUpdateOption option) {
        List<PO> poList = dtoList.stream().map(this::dtoToPO).collect(Collectors.toList());
        int affectedRows;
        if (jdbcDao != null) {
            int[] affectedRowArray = jdbcDao.batchUpdate(poList, option);
            affectedRows = Arrays.stream(affectedRowArray).sum();
        } else {
            affectedRows = getMapper().batchUpdate(poList, option);
        }
        afterBatchInsert(dtoList, poList, affectedRows);
        return affectedRows;
    }
    
    @Override
    public int deleteById(ID id) {
        int affectedRows = getMapper().delete(getDeleteOption(id));
        afterDeleteById(id, affectedRows);
        return affectedRows;
    }
    
    @Override
    public int batchDeleteById(List<ID> idList) {
        int affectedRows = getMapper().delete(getBatchDeleteOption(idList));
        afterBatchDeleteById(idList, affectedRows);
        return affectedRows;
    }
    
    protected InsertOption getInsertOption(DTO dto) {
        return UpdateOptionHelper.getInsertOption(entityClass);
    }
    
    protected UpsertOption getUpsertOption(DTO dto, String... updateFields) {
        return UpdateOptionHelper.getUpsertOption(entityClass).withUpdateFields(updateFields);
    }
    
    protected UpdateOption getUpdateOption(DTO dto, String... updateFields) {
        return UpdateOptionHelper.getUpdateOption(entityClass).eqField(idField()).withUpdateFields(updateFields);
    }
    
    protected BatchInsertOption getBatchInsertOption(List<DTO> dtoList) {
        return UpdateOptionHelper.getBatchInsertOption(entityClass);
    }
    
    protected BatchUpsertOption getBatchUpsertOption(List<DTO> dtoList, String... updateFields) {
        return UpdateOptionHelper.getBatchUpsertOption(entityClass).withUpdateFields(updateFields);
    }
    
    protected BatchUpdateOption getBatchUpdateOption(List<DTO> dtoList, String... updateFields) {
        return UpdateOptionHelper.getBatchUpdateOption(entityClass).eqField(idField()).withUpdateFields(updateFields);
    }
    
    protected DeleteOption getDeleteOption(ID id) {
        return UpdateOptionHelper.getDeleteOption(entityClass).eq(idField(), id);
    }
    
    protected DeleteOption getBatchDeleteOption(List<ID> idList) {
        return UpdateOptionHelper.getDeleteOption(entityClass).in(idField(), idList);
    }
    
    protected void afterInsert(DTO dto, PO po, int affectedRows) {}
    protected void afterUpsert(DTO dto, PO po, int affectedRows) {}
    protected void afterUpdate(DTO dto, PO po, int affectedRows) {}
    protected void afterDeleteById(ID id, int affectedRows) {}
    protected void afterBatchInsert(List<DTO> dtoList, List<PO> poList, int affectedRows) {}
    protected void afterBatchUpsert(List<DTO> dtoList, List<PO> poList, int affectedRows) {}
    protected void afterBatchUpdate(List<DTO> dtoList, List<PO> poList, int affectedRows) {}
    protected void afterBatchDeleteById(List<ID> id, int affectedRows) {}
    
    protected abstract PO dtoToPO(DTO dto);
}
